#include "AlarmItem.hpp"
#include "Clock.hpp"
#include "AssetManager.hpp"
#include "Notification.hpp"
#include "Util.hpp"
#include "States/DefaultState.hpp"
#include "States/DialogState.hpp"
#include <cpp3ds/System/I18n.hpp>
#include <cpp3ds/System/FileSystem.hpp>
#include <TweenEngine/Tween.h>
#include <time.h>
#include <stdlib.h>
#include <cmath>
#ifndef EMULATION
#include "KeyboardApplet.hpp"
#endif

namespace Clock {

AlarmItem::AlarmItem(int defaultHour, int defaultMinute, bool isEnabled, int index)
: m_timeHour(defaultHour)
, m_timeMinute(defaultMinute)
, m_checkAlarm(true)
, m_isAlarmEnabled(isEnabled)
, m_hasCustomMusic(false)
, m_alarmIndex(index)
{
	m_textTimerMinutes.setString("00  ");
	m_textTimerMinutes.setCharacterSize(32);
	m_textTimerMinutes.useSystemFont();
	m_textTimerMinutes.setFillColor(cpp3ds::Color::Black);
	m_textTimerMinutes.setOrigin(m_textTimerMinutes.getLocalBounds().width / 2, m_textTimerMinutes.getLocalBounds().height / 2);

	m_textTimerHours = m_textTimerMinutes;
	m_textTimerHours.setOrigin(m_textTimerHours.getLocalBounds().width, m_textTimerHours.getLocalBounds().height / 2);

	m_textHourTwoPoints.setFillColor(cpp3ds::Color(66, 66, 66));
	m_textHourTwoPoints.useSystemFont();
	m_textHourTwoPoints.setString(":");
	m_textHourTwoPoints.setCharacterSize(24);
	m_textHourTwoPoints.setOrigin(m_textHourTwoPoints.getLocalBounds().width / 2, m_textHourTwoPoints.getLocalBounds().height / 2);

	m_separator.setFillColor(cpp3ds::Color(158, 158, 158));
	m_separator.setSize(cpp3ds::Vector2f(50.f, 1.f));

	m_alarmButton.setFillColor(cpp3ds::Color(245, 245, 245));
	m_alarmButton.setOutlineColor(cpp3ds::Color(158, 158, 158));
	m_alarmButton.setOutlineThickness(1);
	m_alarmButton.setSize(cpp3ds::Vector2f(30.f, 30.f));
	m_alarmButton.setOrigin(0.f, m_alarmButton.getLocalBounds().height / 2);

	m_textResetAlarm.setFillColor(cpp3ds::Color::White);
	m_textResetAlarm.setCharacterSize(16);
	m_textResetAlarm.setOutlineThickness(1);
	m_textResetAlarm.setOutlineColor(cpp3ds::Color::Black);
	m_textResetAlarm.setFont(AssetManager<cpp3ds::Font>::get("fonts/fontawesome.ttf"));
	m_textResetAlarm.setString("\uf00d");
	m_textResetAlarm.setOrigin(m_textResetAlarm.getLocalBounds().width / 2, m_textResetAlarm.getLocalBounds().height / 2);

	m_soundTimerOver.setBuffer(AssetManager<cpp3ds::SoundBuffer>::get("sounds/bip.ogg"));
	m_soundTimerOver.setLoop(true);

	if (m_musicLoop.openFromFile(CLOCK_DIR "/alarm.ogg")) {
		m_hasCustomMusic = true;
		m_musicLoop.setLoop(true);
	}

	setPosition(0.f, 0.f);
}

AlarmItem::~AlarmItem()
{

}

void AlarmItem::draw(cpp3ds::RenderTarget &target, cpp3ds::RenderStates states) const
{
	states.transform *= getTransform();

	target.draw(m_textTimerHours, states);
	target.draw(m_textTimerMinutes, states);

	target.draw(m_textHourTwoPoints, states);

	target.draw(m_separator, states);
	target.draw(m_alarmButton, states);

	if (m_timeHour > 0 || m_timeMinute > 0)
	 target.draw(m_textResetAlarm, states);
}

void AlarmItem::update(float delta)
{
	m_tweenManager.update(delta);

	time_t t = time(NULL);
	struct tm * timeinfo;
	timeinfo = localtime(&t);

	if (m_checkAlarm && isAlarmEnabled()) {
		if (m_timeHour == timeinfo->tm_hour && m_timeMinute == timeinfo->tm_min) {
			m_checkAlarm = false;

			if (m_hasCustomMusic)
				m_musicLoop.play();
			else
				m_soundTimerOver.play();

			g_defaultState->requestStackPush(States::Dialog, false, [=](void *data) mutable {
				auto event = reinterpret_cast<DialogState::Event*>(data);
				if (event->type == DialogState::GetText)
				{
					auto str = reinterpret_cast<cpp3ds::String*>(event->data);
					*str = _("Hey, wake up !\nThe alarm for %02d:%02d is ringing !", m_timeHour, m_timeMinute);
					return true;
				}
				else if (event->type == DialogState::GetTitle) {
					auto str = reinterpret_cast<cpp3ds::String *>(event->data);
					*str = _("Alarm");
					return true;
				}
				else if (event->type == DialogState::ShowCancelButton) {
					auto bol = reinterpret_cast<bool *>(event->data);
					*bol = false;
					return true;
				}
				else if (event->type == DialogState::Response)
				{
					bool *accepted = reinterpret_cast<bool*>(event->data);
					if (*accepted)
					{
						if (m_hasCustomMusic)
							m_musicLoop.stop();
						else
							m_soundTimerOver.stop();
					}
					return true;
				}
				return false;
			});
		}
	}

	if (timeinfo->tm_hour == m_timeHour && timeinfo->tm_min == (m_timeMinute + 1) % 60 && !m_checkAlarm) {
		m_checkAlarm = true;
	}
}

void AlarmItem::processEvent(const cpp3ds::Event& event)
{
	if (event.type == cpp3ds::Event::TouchEnded) {
		if (m_textTimerHours.getGlobalBounds().contains(event.touch.x, event.touch.y)) {
			updateUnit(&m_timeHour);
		} else if (m_textTimerMinutes.getGlobalBounds().contains(event.touch.x, event.touch.y)) {
			updateUnit(&m_timeMinute);
		} else if (m_alarmButton.getGlobalBounds().contains(event.touch.x, event.touch.y)) {
			if (isAlarmEnabled())
				disableAlarm();
			else
				enableAlarm();
		} else if (m_textResetAlarm.getGlobalBounds().contains(event.touch.x, event.touch.y) && (m_timeHour > 0 || m_timeMinute > 0)) {
			setAlarmHour(0);
			setAlarmMinute(0);
			disableAlarm();
		}
	}
}

void AlarmItem::updateTimeTexts()
{
	if (m_timeMinute >= 60) {
		m_timeMinute %= 60;
		m_timeHour++;
	}

	m_timeHour %= 24;

	m_textTimerHours.setString(_("%02d  ", m_timeHour));
	m_textTimerMinutes.setString(_("%02d  ", m_timeMinute));
}

int AlarmItem::getNumberFromUser(int defaultNumber)
{
#ifndef EMULATION
	KeyboardApplet kb(KeyboardApplet::Number, 2);
	if (defaultNumber > 0)
		swkbdSetInitialText(kb, std::to_string(defaultNumber).c_str());
	swkbdSetFeatures(kb, SWKBD_FIXED_WIDTH);
	cpp3ds::String input = kb.getInput();
	if (!input.isEmpty())
		return stoi(input.toAnsiString());
#else
	return 5;
#endif

	return -1;
}

void AlarmItem::updateUnit(int *unit)
{
	int returnedNumber = getNumberFromUser(*unit);
	if (returnedNumber >= 0)
		*unit = returnedNumber;

	resetAlarmCheck();
	updateTimeTexts();
}

void AlarmItem::setAlarmHour(int hour)
{
	m_timeHour = hour;
	updateTimeTexts();
}

void AlarmItem::setAlarmMinute(int minute)
{
	m_timeMinute = minute;
	updateTimeTexts();
}

int AlarmItem::getAlarmHour()
{
	return m_timeHour;
}

int AlarmItem::getAlarmMinute()
{
	return m_timeMinute;
}

void AlarmItem::setPosition(float posX, float posY)
{
	m_textTimerMinutes.setPosition(posX, posY);
	m_textTimerHours.setPosition(m_textTimerMinutes.getPosition().x - (m_textTimerMinutes.getLocalBounds().width / 2) - 5.f, m_textTimerMinutes.getPosition().y);
	m_textHourTwoPoints.setPosition(m_textTimerHours.getPosition().x - m_textTimerHours.getLocalBounds().width / 8, m_textTimerHours.getPosition().y);
	m_separator.setPosition(m_textTimerMinutes.getPosition().x + m_textTimerMinutes.getLocalBounds().width / 2, m_textTimerMinutes.getPosition().y + m_textTimerMinutes.getLocalBounds().height / 2 - 20.f);
	m_alarmButton.setPosition(m_separator.getPosition().x + m_separator.getLocalBounds().width + 20.f, m_separator.getPosition().y);
	m_textResetAlarm.setPosition(315.f - m_textResetAlarm.getLocalBounds().width / 2, m_alarmButton.getPosition().y - m_textResetAlarm.getLocalBounds().width / 2 + 2.f);
}

void AlarmItem::resetAlarmCheck()
{
	m_checkAlarm = true;
}

void AlarmItem::enableAlarm()
{
	m_isAlarmEnabled = true;

	TweenEngine::Tween::to(m_alarmButton, m_alarmButton.FILL_COLOR_RGB, 0.3f)
		.target(g_themeColor.r, g_themeColor.g, g_themeColor.b)
		.start(m_tweenManager);

	TweenEngine::Tween::to(m_alarmButton, m_alarmButton.OUTLINE_COLOR_RGB, 0.3f)
		.target(g_themeColor.r, g_themeColor.g, g_themeColor.b)
		.start(m_tweenManager);

	TweenEngine::Tween::to(m_alarmButton, m_alarmButton.FILL_COLOR_ALPHA, 0.3f)
		.target(128.f)
		.start(m_tweenManager);
}

void AlarmItem::disableAlarm()
{
	m_isAlarmEnabled = false;

	TweenEngine::Tween::to(m_alarmButton, m_alarmButton.FILL_COLOR_RGB, 0.3f)
		.target(245, 245, 245)
		.start(m_tweenManager);

	TweenEngine::Tween::to(m_alarmButton, m_alarmButton.OUTLINE_COLOR_RGB, 0.3f)
		.target(158, 158, 158)
		.start(m_tweenManager);

	TweenEngine::Tween::to(m_alarmButton, m_alarmButton.FILL_COLOR_ALPHA, 0.3f)
		.target(255.f)
		.start(m_tweenManager);
}

bool AlarmItem::isAlarmEnabled()
{
	return m_isAlarmEnabled;
}

void AlarmItem::setIndex(int index)
{
	m_alarmIndex = index;
}

int AlarmItem::getIndex()
{
	return m_alarmIndex;
}

} // namespace Clock
